package je3.nio;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

/**
 * Lock.java: this class demonstrates file locking and simple file read and
 * write operations using java.nio.channels.FileChannel.  It uses file locking
 * to prevent two instances of the program from running at the same time.
 */
public class Lock {
    public static void main(String args[])
            throws IOException, InterruptedException {
        RandomAccessFile file = null; // The file we'll lock
        FileChannel f = null;         // The channel to the file
        FileLock lock = null;         // The lock object we hold

        try {   // The finally clause closes the channel and releases the lock
            // We use a temporary file as the lock file.
            String tmpdir = System.getProperty("java.io.tmpdir");
            String filename = Lock.class.getName() + ".lock";
            File lockfile = new File(tmpdir, filename);

            // Create a FileChannel that can read and write that file.
            // Note that we rely on the java.io package to open the file,
            // in read/write mode, and then just get a channel from it.
            // This will create the file if it doesn't exit.  We'll arrange
            // for it to be deleted below, if we succeed in locking it.
            file = new RandomAccessFile(lockfile, "rw");
            f = file.getChannel();

            // Try to get an exclusive lock on the file.
            // This method will return a lock or null, but will not block.
            // See also FileChannel.lock( ) for a blocking variant.
            lock = f.tryLock();

            if (lock != null) {
                // We obtained the lock, so arrange to delete the file when
                // we're done, and then write the approximate time at which
                // we'll relinquish the lock on the file.
                lockfile.deleteOnExit();  // Just a temporary file

                // First, we need a buffer to hold the timestamp
                ByteBuffer bytes = ByteBuffer.allocate(8); // a long is 8 bytes

                // Put the time in the buffer and flip to prepare for writing
                // Note that many Buffer methods can be "chained" like this.
                bytes.putLong(System.currentTimeMillis() + 10000).flip();

                f.write(bytes); // Write the buffer contents to the channel
                f.force(false); // Force them out to the disk
            } else {
                // We didn't get the lock, which means another instance is
                // running.  First, let the user know this.
                System.out.println("Another instance is already running");

                // Next, we attempt to read the file to figure out how much
                // longer the other instance will be running.  Since we don't
                // have a lock, the read may fail or return inconsistent data.
                try {
                    ByteBuffer bytes = ByteBuffer.allocate(8);
                    f.read(bytes);  // Read 8 bytes from the file
                    bytes.flip();   // Flip buffer before extracting bytes
                    long exittime = bytes.getLong(); // Read bytes as a long
                    // Figure out how long that time is from now and round
                    // it to the nearest second.
                    long secs = (exittime - System.currentTimeMillis() + 500) / 1000;
                    // And tell the user about it.
                    System.out.println("Try again in about " + secs + " seconds");
                }
                catch (IOException e) {
                    // This probably means that locking is enforced by the OS
                    // and we were prevented from reading the file.
                }

                // This is an abnormal exit, so set an exit code.
                System.exit(1);
            }


            // Simulate a real application by sleeping for 10 seconds.
            System.out.println("Starting...");
            Thread.sleep(10000);
            System.out.println("Exiting.");
        }
        finally {
            // Always release the lock and close the file
            // Closing the RandomAccessFile also closes its FileChannel.
            if (lock != null && lock.isValid()) lock.release();
            if (file != null) file.close();
        }
    }
}
